from datetime import datetime, timedelta, timezone
UTC = timezone.utc

from sqlalchemy import select # 引入 select 模块 ！！！
from flask_login import (
    current_user, 
    login_required
)
from flask_restx import (
    Namespace, 
    abort,
    Resource,
    fields
)
from flask import g
from app.apis.serializer import ShortTime, TimeAgo
from app import db, socketio
from app.models import Chatroom, ChatroomMember, ChatroomMessage, UserInfo
import jwt
ns = Namespace('chatrooms', description='聊天室相关操作')


chatroom_model = ns.model(
    'chatroom model',
    {
        'id' : fields.Integer(),
        'name' : fields.String(required=True),
        'is_private' : fields.Boolean(required=True),
        'last_activity_time' : TimeAgo(),
    }
)

chatroom_member_model = ns.model(
    'chatroom member model',
    {
        'id': fields.Integer(),
        'nickname': fields.String(),
        'avatar': fields.String(),
        'is_online': fields.Boolean(),       
    },
)

chatroom_message_model = ns.model(
    'chatroom message model',
    {
        'id': fields.Integer(),
        'type': fields.String(),
        'content': fields.String(),
        'sender_id': fields.Integer(),
        'sender_nickname': fields.String(),
        'sender_avatar': fields.String(),
        'chatroom_id': fields.Integer(),
        'created_at': fields.String(),
        'send_time': ShortTime(attribute='created_at'),
        'is_recalled': fields.Boolean(),
    },
)

def send_room_message(message):
    ### 发送群聊消息 ###

    serialized_message = marshal(message, room_message_model)

    socketio.send(
        serialized_message,
        json = True,
        room = 'room' + str(message.chatroom_id),
        namespace = '/websocket',
    )
def send_room_recalled_message(message):

    ### 发送群聊撤回消息 ###
    serialized_message = marshal(message, room_message_model)

    socketio.emit(
        'message_recalled', # 事件名
        serialized_message,
        room = 'room' + str(message.chatroom_id),
        namespace = '/websocket',
    )

#emit 和 send 的区别:
#emit: 触发事件，发送给指定用户
    #参数：'事件名’，‘数据’
#send: 发送消息，发送给所有用户
    #参数：‘数据’

# Resource 是 Flask-RESTx 中用于定义 API 端点的基类。每个继承自 Resource 的类代表一个具体的 API 资源（如用户、文章、产品等）。
@login_required
@ns.route('')
class Chatrooms(Resource):
    @ns.marshal_with(chatroom_model) # 格式返回的数据结构
    ###​@ns.marshal_with行为​：
    #1. 当返回空列表时，自动序列化为空数组 []
    #2. 非空列表时按chatroom_model格式化
    def get(self):
        '''获取所有聊天室'''
        print('-'*30, "获取所有聊天室", '-'*30)
        
        chatrooms = db.session.scalars(
            select(Chatroom)
            .where(
                Chatroom.is_delete.is_(False),
                Chatroom.is_private.is_(False) |
                (
                    Chatroom.is_private.is_(True) &
                    Chatroom.id.in_(
                        select(ChatroomMember.chatroom_id).
                        where(ChatroomMember.user_id == current_user.id) # 群聊私有，且当前用户是该群的成员
                    )
                )
            )
            .order_by(Chatroom.last_activity_time.desc()) ).all() # 按活跃程度排序
        print(chatrooms)
        print(current_user.id)
        return chatrooms

    @ns.expect(chatroom_model) # 规定请求参数
    @ns.marshal_with(chatroom_model) # 格式化返回结果
    def post(self):
        ### 创建聊天室 ###
        print('-'*30, "创建聊天室", '-'*30)
        data = ns.payload # 获取满足命名空间定义的参数数据，与ns.expect()配合使用
        name, is_private = data['name'], data['is_private']

        chatroom = db.session.scalars(
            select(Chatroom).where(
                Chatroom.name == name, Chatroom.is_delete.is_(False) # 重名且没被删除
            )
        ).first()

        if chatroom:
            print('该聊天室名称已被占用')
            abort(400, message='该聊天室名称已被占用')
        
        # 更新数据库
        user_id = current_user.id
        chatroom = Chatroom(
            name = name,
            creator_id = user_id,
            is_private = is_private,
            last_activity_time = datetime.now()
        )
        db.session.add(chatroom)
        db.session.flush()

        chatroom_member = ChatroomMember(
            chatroom_id = chatroom.id,
            user_id = user_id
        )
        db.session.add(chatroom_member)

        db.session.commit()
        print('chatroom created')
        return chatroom

@login_required
@ns.route('/<int:chatroom_id>/members')
class ChatroomMembers(Resource):
    @ns.marshal_with(chatroom_member_model)
    def get(self, chatroom_id):
        ### 获取聊天室成员列表 ###

        members = db.session.scalars(
            select(UserInfo).
            where(
                UserInfo.id.in_(
                    select(ChatroomMember.user_id).where(
                        ChatroomMember.chatroom_id == chatroom_id,
                        ChatroomMember.is_delete.is_(False)
                    )
                )
            )
        ).all()
        return members

@login_required
@ns.route('/<int:chatroom_id>/invitation_link')
class ChatroomInvitationLink(Resource):
    '''
    聊天室邀请链接
    '''

    def get(self, chatroom_id):
        ###生成聊天室的邀请链接###

        valid_days = int(request.args.get('valid_days', 7))
        # 验证用户设定的邀请期限
        if valid_days > 7:
            abort(400, message = '有效期不超过7天')

        
        payload = {
            'chatroom_id': chatroom_id,
            'exp': datetime.now(UTC) + timedelta(days = valid_days),
        }

        # 加密邀请信息
        key = current_app.config['SECRET_KEY']
        join_token = jwt.encode(payload, key, algorithm = 'HS256')

        return url_for('web.index', join_token = join_token, _external = True)

@login_required
@ns.route('/join')
class JoinChatroom(Resource):
    @ns.marshal_with(chatroom_model)
    def post(self):
        ###通过邀请链接加入聊天室###
        join_token = request.json.get('join_token')

        try:
            key = current_app.config['SECRET_KEY']
            payload = jwt.decode(join_token.encode(), key, algorithms = 'HS256')
        except jwt.ExpiredSignatureError:
            abort(400, '邀请链接已过期')

        chatroom_id = payload['chatroom_id']
        chatroom = db.session.scalars(
            select(Chatroom)
            .where(Chatroom.id == chatroom_id)
        ).first()

        if not chatroom:
            abort(400, '聊天室不存在')
        
        # 添加新成员
        user_id = current_user.id
        chatroom_member = ChatroomMember(chatroom_id=chatroom_id, user_id=user_id)
        db.session.add(chatroom_member)

        # 新增“新成员加入聊天室“的系统消息
        message = ChatroomMessage(
            content = f'{current_user.nickname} 加入聊天室',
            chatroom_id = chatroom_id
        )
        db.session.add(message)

        # 更新聊天室最后活跃时间
        chatroom.last_activity_time = datetime.now()

        db.session.commit()

        # 发送实时消息
        send_room_message(message)

        return chatroom


@login_required
@ns.route('/<int:chatroom_id>/leave')
class ChatroomMessageList(Resource):
    @ns.marshal_with(chatroom_message_model)
    def get(self, chatroom_id):

        # 时间锚点
        before_time = request.args.get('before_time') 

        # 加载条数, 默认30
        limit = 30 

        # 
        query = (
            select(ChatroomMessage).
            where(ChatroomMessage.chatroom_id == chatroom_id).
            order_by(ChatroomMessage.created_at.desc()).
            limit(limit)
        )

        # 增加筛选条件（查看锚点前的消息），若没有传入，则代表首次加载
        if before_time:
            before_time = datetime.strptime(before_time, '%Y-%m-%d %H:%M:%S')
            query = query.where(ChatroomMessage.created_at < before_time)

        # 执行查询
        messages_list = db.session.scalars(query).all()

        return messages_list

    def post(self, chatroom_id):
        ### 聊天室发送消息 ###

        chatroom = db.session.scalars(
            select(Chatroom).where(Chatroom.id == chatroom_id)
        ).first()

        if not chatroom:
            abort(400, "聊天室不存在")
        
        # 添加消息记录
        content = request.json.get('content')\
        
        message = ChatroomMessage(
            chatroom_id=chatroom_id,
            sender_id=current_user.id, 
            content=content,
        )

        db.session.add(message)

        # 更改聊天室最后活动时间
        chatroom.last_activity_time = datetime.now()

        db.session.commit()

        # 发送实时消息
        send_room_message(message)

@login_required
@ns.route('/<int:chatroom_id>/messages/<int:message_id>')
class ChatroomOneMessage(Resource):
    def delete(self, chatroom_id, message_id):
        ### 聊天室撤回消息 ###

        chatroom = db.session.scalars(
            select(Chatroom).
            where(Chatroom.id == chatroom_id)
        ).first()

        if not chatroom:
            abort(400, '聊天室不存在')
        
        message = db.session.scalars(
            select(ChatroomMessage).
            where(ChatroomMessage.id == message_id)
        ).first()

        if not message:
            abort(400, '消息不存在')
        
        # 权限检查（只允许本人撤销）
        if message.sender_id != current_user.id:
            abort(400, '只能撤回自己发送的消息')

        # 发送时间检查
        now = datetime.now()
        if (now - message.created_at).total_seconds() > 60:
            abort(400, '只能撤回一分钟内发送的消息')

        # 撤销消息
        message.is_recalled = True
        message.recalled_time = now

        db.session.commit()

        # 广播撤回消息
        send_room_recalled_message(message)

        